Marine Biodiversity Observation Network Pole to Pole of the Americas (MBON Pole to Pole)

Written by E. Montes () and Eduardo Klein () on Auguts 28, 2020.

This code pulls data from NOAA’s ERDDAP servers and creates time series plots of sea surface temperature (SST) and chlorophyll-a concentration (CHL), and maps showing the latest available data for the selected region.

Step 1

First, let’s load required libraries

library(readr)
library(rerddap)
library(lubridate)
library(dplyr)
library(flexdashboard)
library(reshape2)
library(leaflet)
library(ggplot2)
library(vegan)
library(xts)
library(dygraphs)
library(plotly)
library(mapdata)

library(RColorBrewer)
palette(brewer.pal(8, "Set2"))

Step 2

Query SST data from ERDDAP

## remove all spaces from string
NoSpaces = function(x){
  return(gsub(" ", "", x))
}

## set site coordinates and time for SST extraction
SSTSiteName = "Patagonia"   ## for the resulting file name
SSTcoords.lon = -64.
SSTcoords.lat = -41.7

SSTstartDate = "2002-06-01"

## set climatological date start-end
SSTclimStartDate = "2002-06-01"
SSTclimEndDate = "2012-12-31"

## set dataset source
SSTsource = info("jplMURSST41")

##
## Get sst 
SST <- griddap(SSTsource, 
              time=c(SSTstartDate, "last"),
              longitude = c(SSTcoords.lon,SSTcoords.lon),
              latitude = c(SSTcoords.lat,SSTcoords.lat),
              fields = "analysed_sst",
              fmt = "csv")

SST = SST[,c(1,4)]
names(SST) = c("time", "SST")

## convert time to a Data object
SST$time = as.Date(ymd_hms(SST$time))

Step 3

Calculate SST climatology

SST.clim = SST %>% filter(time>=ymd(SSTclimStartDate), time<=SSTclimEndDate) %>% 
  group_by(yDay = yday(time)) %>% 
  summarise(SST.mean = mean(SST),
            SST.median = median(SST),
            SST.sd = sd(SST),
            SST.q5 = quantile(SST, 0.05),
            SST.q10 = quantile(SST, 0.10),
            SST.q25 = quantile(SST, 0.25),
            SST.q75 = quantile(SST, 0.75),
            SST.q90 = quantile(SST, 0.90),
            SST.q95 = quantile(SST, 0.95),
            SST.min = min(SST),
            SST.max = max(SST))

Step 4

Plot SST time series

SST.xts = as.xts(SST$SST, SST$time)
dygraph(SST.xts, 
        ylab = "Sea Surface Temperature (Deg C)") %>% 
  dySeries("V1", label ="SST (Deg C)", color = "steelblue") %>%
  dyHighlight(highlightCircleSize = 5, 
              highlightSeriesBackgroundAlpha = 0.2,
              hideOnMouseOut = FALSE) %>% 
  dyOptions(fillGraph = FALSE, fillAlpha = 0.4) %>% 
  dyRangeSelector(dateWindow = c(max(SST$time) - years(5), max(SST$time)))


## subset SST for last year
SST.lastyear = SST %>% filter(year(time)==max(year(time)))

## make the plot
pp = ggplot(SST.clim, aes(yDay, SST.mean))
pp = pp + geom_line() + geom_smooth(span=0.25, se=FALSE, colour="steelblue") +  
  geom_ribbon(aes(ymin=SST.q25, ymax=SST.q75), fill="steelblue", alpha=0.5) +
  geom_line(data=SST.lastyear, aes(yday(time), SST), colour="red") + 
  ylab("Sea Surface Temperature (Deg C)") + xlab("Day of the Year") + 
  theme_bw(base_size = 9) 
ggplotly(pp) %>% plotly::config(displayModeBar = F) 
`geom_smooth()` using method = 'loess' and formula 'y ~ x'

Step 5

Save SST time series data

write_csv(SST, path = paste0(NoSpaces(SSTSiteName), "_SST.csv"))
write_csv(SST.clim, path = paste0(NoSpaces(SSTSiteName), "_Climatology.csv"))

Step 6

Create a map of the latest SST data

sstInfo <- info('jplMURSST41')
# get latest 3-day composite sst
GHRSST <- griddap(sstInfo, latitude = c(-60., -20.), longitude = c(-90., -47.), time = c('last','last'), fields = 'analysed_sst')

mycolor <- colors$temperature
w <- map_data("worldHires", ylim = c(-60., -20.), xlim = c(-90., -47.))
ggplot(data = GHRSST$data, aes(x = lon, y = lat, fill = analysed_sst)) + 
  geom_polygon(data = w, aes(x = long, y = lat, group = group), fill = "grey80") +
  geom_raster(interpolate = FALSE) +
  scale_fill_gradientn(colours = mycolor, na.value = NA) +
  theme_bw() + ylab("latitude") + xlab("longitude") +
  coord_fixed(1.3, xlim = c(-90., -47.),  ylim = c(-60., -20.)) + ggtitle("Latest daily SST data")

Step 7

Query CHL data from ERDDAP

## remove all spaces from string
NoSpaces = function(x){
  return(gsub(" ", "", x))
}

## set site coordinates and time for CHL extraction
CHLSiteName = "Golfo Nuevo"   ## for the resulting file name
CHLcoords.lon = -74
CHLcoords.lat = 12

CHLstartDate = "2012-01-01"

## set climatological date start-end
CHLclimStartDate = "2012-01-01"
CHLclimEndDate = "2016-12-31"

## set dataset source
CHLsource = info("erdMH1chla8day")

##
## Get CHL 
CHL <- griddap(CHLsource, 
               time=c(CHLstartDate, "last"),
               longitude = c(CHLcoords.lon,CHLcoords.lon),
               latitude = c(CHLcoords.lat,CHLcoords.lat),
               fields = "chlorophyll", fmt = "csv")

CHL = CHL[,c(1,4)]
names(CHL) = c("time", "CHL")
CHL = na.omit(CHL)

## convert time to a Data object
CHL$time = as.Date(ymd_hms(CHL$time))

Step 8

Calculate CHL climatology

CHL.clim = CHL %>% filter(time>=ymd(CHLclimStartDate), time<=CHLclimEndDate) %>% 
  group_by(yDay = yday(time)) %>% 
  summarise(CHL.mean = mean(CHL),
            CHL.median = median(CHL),
            CHL.sd = sd(CHL),
            CHL.q5 = quantile(CHL, 0.05),
            CHL.q10 = quantile(CHL, 0.10),
            CHL.q25 = quantile(CHL, 0.25),
            CHL.q75 = quantile(CHL, 0.75),
            CHL.q90 = quantile(CHL, 0.90),
            CHL.q95 = quantile(CHL, 0.95),
            CHL.min = min(CHL),
            CHL.max = max(CHL))

Step 9

Plot CHL time series

CHL.xts = as.xts(CHL$CHL, CHL$time)
dygraph(CHL.xts, 
        ylab = "Chlorophyll a (mg m-3)") %>% 
  dySeries("V1", label ="CHL", color = "steelblue") %>%
  dyHighlight(highlightCircleSize = 5, 
              highlightSeriesBackgroundAlpha = 0.2,
              hideOnMouseOut = FALSE) %>% 
  dyOptions(fillGraph = FALSE, fillAlpha = 0.4) %>% 
  dyRangeSelector(dateWindow = c(max(CHL$time) - years(5), max(CHL$time)))


### CHL Last year with smoothed Climatology {data-width=250}

## subset CHL for last year
CHL.lastyear = CHL %>% filter(year(time)==max(year(time)))

## make the plot
pp = ggplot(CHL.clim, aes(yDay, CHL.mean))
pp = pp + geom_line() + geom_smooth(span=0.25, se=FALSE, colour="steelblue") +  
  geom_ribbon(aes(ymin=CHL.q25, ymax=CHL.q75), fill="steelblue", alpha=0.5) +
  geom_line(data=CHL.lastyear, aes(yday(time), CHL), colour="red") + 
  ylab("Chlorophyll a (mg m-3)") + xlab("Day of the Year") + 
  theme_bw(base_size = 9) 
ggplotly(pp) %>% plotly::config(displayModeBar = F) 
`geom_smooth()` using method = 'loess' and formula 'y ~ x'

#Step 10 Save CHL time series data

write_csv(CHL, path = paste0(NoSpaces(CHLSiteName), "_CHL.csv"))
write_csv(CHL.clim, path = paste0(NoSpaces(CHLSiteName), "_Climatology.csv"))

#Step 11 Create a map of the latest CHL data

require("rerddap")
require("ggplot2")
require("mapdata")

# get latest Monthly chl (VIIRS)
chlaInfo <- info('nesdisVHNSQchlaMonthly')
viirsCHLA <- griddap(chlaInfo, latitude = c(-20., -60.), longitude = c(-90., -47.), time = c('last','last'), fields = 'chlor_a')

# get latest 8-day chl (MODIS)
chlaInfo_8d <- info('erdMH1chla8day')
MODIS_CHLA_8d <- griddap(chlaInfo_8d, latitude = c(-20., -60.), longitude = c(-90., -47.), time = c('last','last'), fields = 'chlorophyll')

# Map monthly chl (VIIRS)
mycolor <- colors$chlorophyll
w <- map_data("worldHires", ylim = c(-60., -20.), xlim = c(-90., -47.))
ggplot(data = viirsCHLA$data, aes(x = lon, y = lat, fill = log(chlor_a))) + 
  geom_polygon(data = w, aes(x = long, y = lat, group = group), fill = "grey80") +
  geom_raster(interpolate = FALSE) +
  scale_fill_gradientn(colours = mycolor, na.value = NA) +
  theme_bw() + ylab("latitude") + xlab("longitude") +
  coord_fixed(1.3, xlim = c(-90., -47.),  ylim = c(-60., -20.)) + ggtitle("Latest VIIRS Monthly Chla")


# Map 8-day chl (MODIS)
mycolor <- colors$chlorophyll
w <- map_data("worldHires", ylim = c(-60., -20.), xlim = c(-90., -47.))
ggplot(data = MODIS_CHLA_8d$data, aes(x = lon, y = lat, fill = log(chlorophyll))) + 
  geom_polygon(data = w, aes(x = long, y = lat, group = group), fill = "grey80") +
  geom_raster(interpolate = FALSE) +
  scale_fill_gradientn(colours = mycolor, na.value = NA) +
  theme_bw() + ylab("latitude") + xlab("longitude") +
  coord_fixed(1.3, xlim = c(-90., -47.),  ylim = c(-60., -20.)) + ggtitle("Latest MODIS 8-day Chla")

LS0tCnRpdGxlOiAiU2F0ZWxsaXRlIFNTVCBhbmQgQ0hMIGRhdGEgZXh0cmFjdGlvbnMgZnJvbSBzZWxlY3RlZCBsb2NhdGlvbnMiCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCiMjIE1hcmluZSBCaW9kaXZlcnNpdHkgT2JzZXJ2YXRpb24gTmV0d29yayBQb2xlIHRvIFBvbGUgb2YgdGhlIEFtZXJpY2FzIChNQk9OIFBvbGUgdG8gUG9sZSkKCldyaXR0ZW4gYnkgRS4gTW9udGVzIChlbW9udGVzaEB1c2YuZWR1KSBhbmQgRWR1YXJkbyBLbGVpbiAoZWtsZWluQHVzYi52ZSkgb24gQXVndXRzIDI4LCAyMDIwLgoKVGhpcyBjb2RlIHB1bGxzIGRhdGEgZnJvbSBOT0FBJ3MgW0VSRERBUF0oaHR0cHM6Ly9jb2FzdHdhdGNoLnBmZWcubm9hYS5nb3YvZXJkZGFwL2luZGV4Lmh0bWwpIHNlcnZlcnMgYW5kIGNyZWF0ZXMgdGltZSBzZXJpZXMgcGxvdHMgb2Ygc2VhIHN1cmZhY2UgdGVtcGVyYXR1cmUgKFNTVCkgYW5kIGNobG9yb3BoeWxsLWEgY29uY2VudHJhdGlvbiAoQ0hMKSwgYW5kIG1hcHMgc2hvd2luZyB0aGUgbGF0ZXN0IGF2YWlsYWJsZSBkYXRhIGZvciB0aGUgc2VsZWN0ZWQgcmVnaW9uLgoKIyBTdGVwIDEKRmlyc3QsIGxldCdzIGxvYWQgcmVxdWlyZWQgbGlicmFyaWVzCmBgYHtyfQpsaWJyYXJ5KHJlYWRyKQpsaWJyYXJ5KHJlcmRkYXApCmxpYnJhcnkobHVicmlkYXRlKQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KGZsZXhkYXNoYm9hcmQpCmxpYnJhcnkocmVzaGFwZTIpCmxpYnJhcnkobGVhZmxldCkKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KHZlZ2FuKQpsaWJyYXJ5KHh0cykKbGlicmFyeShkeWdyYXBocykKbGlicmFyeShwbG90bHkpCmxpYnJhcnkobWFwZGF0YSkKCmxpYnJhcnkoUkNvbG9yQnJld2VyKQpwYWxldHRlKGJyZXdlci5wYWwoOCwgIlNldDIiKSkKYGBgCgojIFN0ZXAgMgpRdWVyeSBTU1QgZGF0YSBmcm9tIEVSRERBUApgYGB7cn0KIyMgcmVtb3ZlIGFsbCBzcGFjZXMgZnJvbSBzdHJpbmcKTm9TcGFjZXMgPSBmdW5jdGlvbih4KXsKICByZXR1cm4oZ3N1YigiICIsICIiLCB4KSkKfQoKIyMgc2V0IHNpdGUgY29vcmRpbmF0ZXMgYW5kIHRpbWUgZm9yIFNTVCBleHRyYWN0aW9uClNTVFNpdGVOYW1lID0gIlBhdGFnb25pYSIgICAjIyBmb3IgdGhlIHJlc3VsdGluZyBmaWxlIG5hbWUKU1NUY29vcmRzLmxvbiA9IC02NC4KU1NUY29vcmRzLmxhdCA9IC00MS43CgpTU1RzdGFydERhdGUgPSAiMjAwMi0wNi0wMSIKCiMjIHNldCBjbGltYXRvbG9naWNhbCBkYXRlIHN0YXJ0LWVuZApTU1RjbGltU3RhcnREYXRlID0gIjIwMDItMDYtMDEiClNTVGNsaW1FbmREYXRlID0gIjIwMTItMTItMzEiCgojIyBzZXQgZGF0YXNldCBzb3VyY2UKU1NUc291cmNlID0gaW5mbygianBsTVVSU1NUNDEiKQoKIyMKIyMgR2V0IHNzdCAKU1NUIDwtIGdyaWRkYXAoU1NUc291cmNlLCAKICAgICAgICAgICAgICB0aW1lPWMoU1NUc3RhcnREYXRlLCAibGFzdCIpLAogICAgICAgICAgICAgIGxvbmdpdHVkZSA9IGMoU1NUY29vcmRzLmxvbixTU1Rjb29yZHMubG9uKSwKICAgICAgICAgICAgICBsYXRpdHVkZSA9IGMoU1NUY29vcmRzLmxhdCxTU1Rjb29yZHMubGF0KSwKICAgICAgICAgICAgICBmaWVsZHMgPSAiYW5hbHlzZWRfc3N0IiwKICAgICAgICAgICAgICBmbXQgPSAiY3N2IikKClNTVCA9IFNTVFssYygxLDQpXQpuYW1lcyhTU1QpID0gYygidGltZSIsICJTU1QiKQoKIyMgY29udmVydCB0aW1lIHRvIGEgRGF0YSBvYmplY3QKU1NUJHRpbWUgPSBhcy5EYXRlKHltZF9obXMoU1NUJHRpbWUpKQoKYGBgCgojIFN0ZXAgMwpDYWxjdWxhdGUgU1NUIGNsaW1hdG9sb2d5CmBgYHtyfQpTU1QuY2xpbSA9IFNTVCAlPiUgZmlsdGVyKHRpbWU+PXltZChTU1RjbGltU3RhcnREYXRlKSwgdGltZTw9U1NUY2xpbUVuZERhdGUpICU+JSAKICBncm91cF9ieSh5RGF5ID0geWRheSh0aW1lKSkgJT4lIAogIHN1bW1hcmlzZShTU1QubWVhbiA9IG1lYW4oU1NUKSwKICAgICAgICAgICAgU1NULm1lZGlhbiA9IG1lZGlhbihTU1QpLAogICAgICAgICAgICBTU1Quc2QgPSBzZChTU1QpLAogICAgICAgICAgICBTU1QucTUgPSBxdWFudGlsZShTU1QsIDAuMDUpLAogICAgICAgICAgICBTU1QucTEwID0gcXVhbnRpbGUoU1NULCAwLjEwKSwKICAgICAgICAgICAgU1NULnEyNSA9IHF1YW50aWxlKFNTVCwgMC4yNSksCiAgICAgICAgICAgIFNTVC5xNzUgPSBxdWFudGlsZShTU1QsIDAuNzUpLAogICAgICAgICAgICBTU1QucTkwID0gcXVhbnRpbGUoU1NULCAwLjkwKSwKICAgICAgICAgICAgU1NULnE5NSA9IHF1YW50aWxlKFNTVCwgMC45NSksCiAgICAgICAgICAgIFNTVC5taW4gPSBtaW4oU1NUKSwKICAgICAgICAgICAgU1NULm1heCA9IG1heChTU1QpKQpgYGAKCiMgU3RlcCA0ClBsb3QgU1NUIHRpbWUgc2VyaWVzCmBgYHtyfQpTU1QueHRzID0gYXMueHRzKFNTVCRTU1QsIFNTVCR0aW1lKQpkeWdyYXBoKFNTVC54dHMsIAogICAgICAgIHlsYWIgPSAiU2VhIFN1cmZhY2UgVGVtcGVyYXR1cmUgKERlZyBDKSIpICU+JSAKICBkeVNlcmllcygiVjEiLCBsYWJlbCA9IlNTVCAoRGVnIEMpIiwgY29sb3IgPSAic3RlZWxibHVlIikgJT4lCiAgZHlIaWdobGlnaHQoaGlnaGxpZ2h0Q2lyY2xlU2l6ZSA9IDUsIAogICAgICAgICAgICAgIGhpZ2hsaWdodFNlcmllc0JhY2tncm91bmRBbHBoYSA9IDAuMiwKICAgICAgICAgICAgICBoaWRlT25Nb3VzZU91dCA9IEZBTFNFKSAlPiUgCiAgZHlPcHRpb25zKGZpbGxHcmFwaCA9IEZBTFNFLCBmaWxsQWxwaGEgPSAwLjQpICU+JSAKICBkeVJhbmdlU2VsZWN0b3IoZGF0ZVdpbmRvdyA9IGMobWF4KFNTVCR0aW1lKSAtIHllYXJzKDUpLCBtYXgoU1NUJHRpbWUpKSkKCiMjIHN1YnNldCBTU1QgZm9yIGxhc3QgeWVhcgpTU1QubGFzdHllYXIgPSBTU1QgJT4lIGZpbHRlcih5ZWFyKHRpbWUpPT1tYXgoeWVhcih0aW1lKSkpCgojIyBtYWtlIHRoZSBwbG90CnBwID0gZ2dwbG90KFNTVC5jbGltLCBhZXMoeURheSwgU1NULm1lYW4pKQpwcCA9IHBwICsgZ2VvbV9saW5lKCkgKyBnZW9tX3Ntb290aChzcGFuPTAuMjUsIHNlPUZBTFNFLCBjb2xvdXI9InN0ZWVsYmx1ZSIpICsgIAogIGdlb21fcmliYm9uKGFlcyh5bWluPVNTVC5xMjUsIHltYXg9U1NULnE3NSksIGZpbGw9InN0ZWVsYmx1ZSIsIGFscGhhPTAuNSkgKwogIGdlb21fbGluZShkYXRhPVNTVC5sYXN0eWVhciwgYWVzKHlkYXkodGltZSksIFNTVCksIGNvbG91cj0icmVkIikgKyAKICB5bGFiKCJTZWEgU3VyZmFjZSBUZW1wZXJhdHVyZSAoRGVnIEMpIikgKyB4bGFiKCJEYXkgb2YgdGhlIFllYXIiKSArIAogIHRoZW1lX2J3KGJhc2Vfc2l6ZSA9IDkpIApnZ3Bsb3RseShwcCkgJT4lIHBsb3RseTo6Y29uZmlnKGRpc3BsYXlNb2RlQmFyID0gRikgCmBgYAoKIyBTdGVwIDUKU2F2ZSBTU1QgdGltZSBzZXJpZXMgZGF0YQpgYGB7cn0Kd3JpdGVfY3N2KFNTVCwgcGF0aCA9IHBhc3RlMChOb1NwYWNlcyhTU1RTaXRlTmFtZSksICJfU1NULmNzdiIpKQp3cml0ZV9jc3YoU1NULmNsaW0sIHBhdGggPSBwYXN0ZTAoTm9TcGFjZXMoU1NUU2l0ZU5hbWUpLCAiX0NsaW1hdG9sb2d5LmNzdiIpKQpgYGAKCiMgU3RlcCA2CkNyZWF0ZSBhIG1hcCBvZiB0aGUgbGF0ZXN0IFNTVCBkYXRhCmBgYHtyfQpzc3RJbmZvIDwtIGluZm8oJ2pwbE1VUlNTVDQxJykKIyBnZXQgbGF0ZXN0IDMtZGF5IGNvbXBvc2l0ZSBzc3QKR0hSU1NUIDwtIGdyaWRkYXAoc3N0SW5mbywgbGF0aXR1ZGUgPSBjKC02MC4sIC0yMC4pLCBsb25naXR1ZGUgPSBjKC05MC4sIC00Ny4pLCB0aW1lID0gYygnbGFzdCcsJ2xhc3QnKSwgZmllbGRzID0gJ2FuYWx5c2VkX3NzdCcpCgpteWNvbG9yIDwtIGNvbG9ycyR0ZW1wZXJhdHVyZQp3IDwtIG1hcF9kYXRhKCJ3b3JsZEhpcmVzIiwgeWxpbSA9IGMoLTYwLiwgLTIwLiksIHhsaW0gPSBjKC05MC4sIC00Ny4pKQpnZ3Bsb3QoZGF0YSA9IEdIUlNTVCRkYXRhLCBhZXMoeCA9IGxvbiwgeSA9IGxhdCwgZmlsbCA9IGFuYWx5c2VkX3NzdCkpICsgCiAgZ2VvbV9wb2x5Z29uKGRhdGEgPSB3LCBhZXMoeCA9IGxvbmcsIHkgPSBsYXQsIGdyb3VwID0gZ3JvdXApLCBmaWxsID0gImdyZXk4MCIpICsKICBnZW9tX3Jhc3RlcihpbnRlcnBvbGF0ZSA9IEZBTFNFKSArCiAgc2NhbGVfZmlsbF9ncmFkaWVudG4oY29sb3VycyA9IG15Y29sb3IsIG5hLnZhbHVlID0gTkEpICsKICB0aGVtZV9idygpICsgeWxhYigibGF0aXR1ZGUiKSArIHhsYWIoImxvbmdpdHVkZSIpICsKICBjb29yZF9maXhlZCgxLjMsIHhsaW0gPSBjKC05MC4sIC00Ny4pLCAgeWxpbSA9IGMoLTYwLiwgLTIwLikpICsgZ2d0aXRsZSgiTGF0ZXN0IGRhaWx5IFNTVCBkYXRhIikKYGBgCgoKIyBTdGVwIDcKUXVlcnkgQ0hMIGRhdGEgZnJvbSBFUkREQVAKYGBge3J9CiMjIHJlbW92ZSBhbGwgc3BhY2VzIGZyb20gc3RyaW5nCk5vU3BhY2VzID0gZnVuY3Rpb24oeCl7CiAgcmV0dXJuKGdzdWIoIiAiLCAiIiwgeCkpCn0KCiMjIHNldCBzaXRlIGNvb3JkaW5hdGVzIGFuZCB0aW1lIGZvciBDSEwgZXh0cmFjdGlvbgpDSExTaXRlTmFtZSA9ICJHb2xmbyBOdWV2byIgICAjIyBmb3IgdGhlIHJlc3VsdGluZyBmaWxlIG5hbWUKQ0hMY29vcmRzLmxvbiA9IC03NApDSExjb29yZHMubGF0ID0gMTIKCkNITHN0YXJ0RGF0ZSA9ICIyMDEyLTAxLTAxIgoKIyMgc2V0IGNsaW1hdG9sb2dpY2FsIGRhdGUgc3RhcnQtZW5kCkNITGNsaW1TdGFydERhdGUgPSAiMjAxMi0wMS0wMSIKQ0hMY2xpbUVuZERhdGUgPSAiMjAxNi0xMi0zMSIKCiMjIHNldCBkYXRhc2V0IHNvdXJjZQpDSExzb3VyY2UgPSBpbmZvKCJlcmRNSDFjaGxhOGRheSIpCgojIwojIyBHZXQgQ0hMIApDSEwgPC0gZ3JpZGRhcChDSExzb3VyY2UsIAogICAgICAgICAgICAgICB0aW1lPWMoQ0hMc3RhcnREYXRlLCAibGFzdCIpLAogICAgICAgICAgICAgICBsb25naXR1ZGUgPSBjKENITGNvb3Jkcy5sb24sQ0hMY29vcmRzLmxvbiksCiAgICAgICAgICAgICAgIGxhdGl0dWRlID0gYyhDSExjb29yZHMubGF0LENITGNvb3Jkcy5sYXQpLAogICAgICAgICAgICAgICBmaWVsZHMgPSAiY2hsb3JvcGh5bGwiLCBmbXQgPSAiY3N2IikKCkNITCA9IENITFssYygxLDQpXQpuYW1lcyhDSEwpID0gYygidGltZSIsICJDSEwiKQpDSEwgPSBuYS5vbWl0KENITCkKCiMjIGNvbnZlcnQgdGltZSB0byBhIERhdGEgb2JqZWN0CkNITCR0aW1lID0gYXMuRGF0ZSh5bWRfaG1zKENITCR0aW1lKSkKCmBgYAoKIyBTdGVwIDgKQ2FsY3VsYXRlIENITCBjbGltYXRvbG9neQpgYGB7cn0KQ0hMLmNsaW0gPSBDSEwgJT4lIGZpbHRlcih0aW1lPj15bWQoQ0hMY2xpbVN0YXJ0RGF0ZSksIHRpbWU8PUNITGNsaW1FbmREYXRlKSAlPiUgCiAgZ3JvdXBfYnkoeURheSA9IHlkYXkodGltZSkpICU+JSAKICBzdW1tYXJpc2UoQ0hMLm1lYW4gPSBtZWFuKENITCksCiAgICAgICAgICAgIENITC5tZWRpYW4gPSBtZWRpYW4oQ0hMKSwKICAgICAgICAgICAgQ0hMLnNkID0gc2QoQ0hMKSwKICAgICAgICAgICAgQ0hMLnE1ID0gcXVhbnRpbGUoQ0hMLCAwLjA1KSwKICAgICAgICAgICAgQ0hMLnExMCA9IHF1YW50aWxlKENITCwgMC4xMCksCiAgICAgICAgICAgIENITC5xMjUgPSBxdWFudGlsZShDSEwsIDAuMjUpLAogICAgICAgICAgICBDSEwucTc1ID0gcXVhbnRpbGUoQ0hMLCAwLjc1KSwKICAgICAgICAgICAgQ0hMLnE5MCA9IHF1YW50aWxlKENITCwgMC45MCksCiAgICAgICAgICAgIENITC5xOTUgPSBxdWFudGlsZShDSEwsIDAuOTUpLAogICAgICAgICAgICBDSEwubWluID0gbWluKENITCksCiAgICAgICAgICAgIENITC5tYXggPSBtYXgoQ0hMKSkKYGBgCgoKIyBTdGVwIDkKUGxvdCBDSEwgdGltZSBzZXJpZXMKYGBge3J9CkNITC54dHMgPSBhcy54dHMoQ0hMJENITCwgQ0hMJHRpbWUpCmR5Z3JhcGgoQ0hMLnh0cywgCiAgICAgICAgeWxhYiA9ICJDaGxvcm9waHlsbCBhIChtZyBtLTMpIikgJT4lIAogIGR5U2VyaWVzKCJWMSIsIGxhYmVsID0iQ0hMIiwgY29sb3IgPSAic3RlZWxibHVlIikgJT4lCiAgZHlIaWdobGlnaHQoaGlnaGxpZ2h0Q2lyY2xlU2l6ZSA9IDUsIAogICAgICAgICAgICAgIGhpZ2hsaWdodFNlcmllc0JhY2tncm91bmRBbHBoYSA9IDAuMiwKICAgICAgICAgICAgICBoaWRlT25Nb3VzZU91dCA9IEZBTFNFKSAlPiUgCiAgZHlPcHRpb25zKGZpbGxHcmFwaCA9IEZBTFNFLCBmaWxsQWxwaGEgPSAwLjQpICU+JSAKICBkeVJhbmdlU2VsZWN0b3IoZGF0ZVdpbmRvdyA9IGMobWF4KENITCR0aW1lKSAtIHllYXJzKDUpLCBtYXgoQ0hMJHRpbWUpKSkKCiMjIyBDSEwgTGFzdCB5ZWFyIHdpdGggc21vb3RoZWQgQ2xpbWF0b2xvZ3kge2RhdGEtd2lkdGg9MjUwfQoKIyMgc3Vic2V0IENITCBmb3IgbGFzdCB5ZWFyCkNITC5sYXN0eWVhciA9IENITCAlPiUgZmlsdGVyKHllYXIodGltZSk9PW1heCh5ZWFyKHRpbWUpKSkKCiMjIG1ha2UgdGhlIHBsb3QKcHAgPSBnZ3Bsb3QoQ0hMLmNsaW0sIGFlcyh5RGF5LCBDSEwubWVhbikpCnBwID0gcHAgKyBnZW9tX2xpbmUoKSArIGdlb21fc21vb3RoKHNwYW49MC4yNSwgc2U9RkFMU0UsIGNvbG91cj0ic3RlZWxibHVlIikgKyAgCiAgZ2VvbV9yaWJib24oYWVzKHltaW49Q0hMLnEyNSwgeW1heD1DSEwucTc1KSwgZmlsbD0ic3RlZWxibHVlIiwgYWxwaGE9MC41KSArCiAgZ2VvbV9saW5lKGRhdGE9Q0hMLmxhc3R5ZWFyLCBhZXMoeWRheSh0aW1lKSwgQ0hMKSwgY29sb3VyPSJyZWQiKSArIAogIHlsYWIoIkNobG9yb3BoeWxsIGEgKG1nIG0tMykiKSArIHhsYWIoIkRheSBvZiB0aGUgWWVhciIpICsgCiAgdGhlbWVfYncoYmFzZV9zaXplID0gOSkgCmdncGxvdGx5KHBwKSAlPiUgcGxvdGx5Ojpjb25maWcoZGlzcGxheU1vZGVCYXIgPSBGKSAKCmBgYAoKI1N0ZXAgMTAKU2F2ZSBDSEwgdGltZSBzZXJpZXMgZGF0YQpgYGB7cn0Kd3JpdGVfY3N2KENITCwgcGF0aCA9IHBhc3RlMChOb1NwYWNlcyhDSExTaXRlTmFtZSksICJfQ0hMLmNzdiIpKQp3cml0ZV9jc3YoQ0hMLmNsaW0sIHBhdGggPSBwYXN0ZTAoTm9TcGFjZXMoQ0hMU2l0ZU5hbWUpLCAiX0NsaW1hdG9sb2d5LmNzdiIpKQpgYGAKCiNTdGVwIDExCkNyZWF0ZSBhIG1hcCBvZiB0aGUgbGF0ZXN0IENITCBkYXRhCmBgYHtyfQpyZXF1aXJlKCJyZXJkZGFwIikKcmVxdWlyZSgiZ2dwbG90MiIpCnJlcXVpcmUoIm1hcGRhdGEiKQoKIyBnZXQgbGF0ZXN0IE1vbnRobHkgY2hsIChWSUlSUykKY2hsYUluZm8gPC0gaW5mbygnbmVzZGlzVkhOU1FjaGxhTW9udGhseScpCnZpaXJzQ0hMQSA8LSBncmlkZGFwKGNobGFJbmZvLCBsYXRpdHVkZSA9IGMoLTIwLiwgLTYwLiksIGxvbmdpdHVkZSA9IGMoLTkwLiwgLTQ3LiksIHRpbWUgPSBjKCdsYXN0JywnbGFzdCcpLCBmaWVsZHMgPSAnY2hsb3JfYScpCgojIGdldCBsYXRlc3QgOC1kYXkgY2hsIChNT0RJUykKY2hsYUluZm9fOGQgPC0gaW5mbygnZXJkTUgxY2hsYThkYXknKQpNT0RJU19DSExBXzhkIDwtIGdyaWRkYXAoY2hsYUluZm9fOGQsIGxhdGl0dWRlID0gYygtMjAuLCAtNjAuKSwgbG9uZ2l0dWRlID0gYygtOTAuLCAtNDcuKSwgdGltZSA9IGMoJ2xhc3QnLCdsYXN0JyksIGZpZWxkcyA9ICdjaGxvcm9waHlsbCcpCgojIE1hcCBtb250aGx5IGNobCAoVklJUlMpCm15Y29sb3IgPC0gY29sb3JzJGNobG9yb3BoeWxsCncgPC0gbWFwX2RhdGEoIndvcmxkSGlyZXMiLCB5bGltID0gYygtNjAuLCAtMjAuKSwgeGxpbSA9IGMoLTkwLiwgLTQ3LikpCmdncGxvdChkYXRhID0gdmlpcnNDSExBJGRhdGEsIGFlcyh4ID0gbG9uLCB5ID0gbGF0LCBmaWxsID0gbG9nKGNobG9yX2EpKSkgKyAKICBnZW9tX3BvbHlnb24oZGF0YSA9IHcsIGFlcyh4ID0gbG9uZywgeSA9IGxhdCwgZ3JvdXAgPSBncm91cCksIGZpbGwgPSAiZ3JleTgwIikgKwogIGdlb21fcmFzdGVyKGludGVycG9sYXRlID0gRkFMU0UpICsKICBzY2FsZV9maWxsX2dyYWRpZW50bihjb2xvdXJzID0gbXljb2xvciwgbmEudmFsdWUgPSBOQSkgKwogIHRoZW1lX2J3KCkgKyB5bGFiKCJsYXRpdHVkZSIpICsgeGxhYigibG9uZ2l0dWRlIikgKwogIGNvb3JkX2ZpeGVkKDEuMywgeGxpbSA9IGMoLTkwLiwgLTQ3LiksICB5bGltID0gYygtNjAuLCAtMjAuKSkgKyBnZ3RpdGxlKCJMYXRlc3QgVklJUlMgTW9udGhseSBDaGxhIikKCiMgTWFwIDgtZGF5IGNobCAoTU9ESVMpCm15Y29sb3IgPC0gY29sb3JzJGNobG9yb3BoeWxsCncgPC0gbWFwX2RhdGEoIndvcmxkSGlyZXMiLCB5bGltID0gYygtNjAuLCAtMjAuKSwgeGxpbSA9IGMoLTkwLiwgLTQ3LikpCmdncGxvdChkYXRhID0gTU9ESVNfQ0hMQV84ZCRkYXRhLCBhZXMoeCA9IGxvbiwgeSA9IGxhdCwgZmlsbCA9IGxvZyhjaGxvcm9waHlsbCkpKSArIAogIGdlb21fcG9seWdvbihkYXRhID0gdywgYWVzKHggPSBsb25nLCB5ID0gbGF0LCBncm91cCA9IGdyb3VwKSwgZmlsbCA9ICJncmV5ODAiKSArCiAgZ2VvbV9yYXN0ZXIoaW50ZXJwb2xhdGUgPSBGQUxTRSkgKwogIHNjYWxlX2ZpbGxfZ3JhZGllbnRuKGNvbG91cnMgPSBteWNvbG9yLCBuYS52YWx1ZSA9IE5BKSArCiAgdGhlbWVfYncoKSArIHlsYWIoImxhdGl0dWRlIikgKyB4bGFiKCJsb25naXR1ZGUiKSArCiAgY29vcmRfZml4ZWQoMS4zLCB4bGltID0gYygtOTAuLCAtNDcuKSwgIHlsaW0gPSBjKC02MC4sIC0yMC4pKSArIGdndGl0bGUoIkxhdGVzdCBNT0RJUyA4LWRheSBDaGxhIikKYGBgCgo=